import java.awt.Color;
import java.util.Random;
import java.util.Vector;

import uchicago.src.sim.gui.Drawable;
import uchicago.src.sim.gui.SimGraphics;
import uchicago.src.sim.space.Object2DTorus;

public class Firefighter implements Drawable {

	// constants
	private static final Color firefighterNormalColor = Color.WHITE;
	private static final Color firefighterDiedColor = Color.BLACK;
	private static final Color firefighterFirebreakColor = Color.MAGENTA;
	private static final int maxIdleTicks = 3;
    private static final int maxLifeTicks = 5;    
    private static Random random = new Random();
    
    // statics
    private static String sWind;

    // variables
    private Point p;    
    private Color color;
    private Point pgoto;
    private Object2DTorus space;
    private int idleTicks;
    private int lifeTicks;
    private boolean firebreakStarted = false;
    private boolean firebreakFinished = false;
                    
    //firebreak
    private Point pfbi; 
    private Point pfbf; 
    private boolean firebreak;
        
    public Firefighter(Point p, Object2DTorus space) {
        
    	this.p = p;
        this.space = space;
        this.color = firefighterNormalColor;
        this.idleTicks = 0;
        this.lifeTicks = maxLifeTicks;
        
        Cell c = (Cell) space.getObjectAt(p.x, p.y);
        c.setHasFirefighter(true);
    }
    
    public void action() {
		
		if (isDead())
			return;

		if (isOnBurningCell())
			decreaseLife();
		
		if (isFirebreaker()) 
			firebreak();			

		if (hasGoTo()) {
			goTo();
			idleTicks = 0;
			return;
		}
		
		if (hasFire()) {
			fightFire();
			idleTicks = 0;
			return;
		}
		
		idleTicks++;		
	}
    
    public void cut() {
    	
    	Cell c = (Cell) space.getObjectAt(p.x, p.y);
    	c.increaseCutTicks();
    	if (c.isCut())    	
    		c.setCut();

    	if (windIsDiagonal()) {
	    	Cell c2 = (Cell) space.getObjectAt(p.x, p.y+1);
	    	c2.increaseCutTicks();
	    	if (c2.isCut()) {
	    		c2.setCut();
	    	}
    	}
    	
    }
    
    public boolean windIsDiagonal() {
    	return sWind.equals("NE") || sWind.equals("NW") || sWind.equals("SE") || sWind.equals("SW"); 
    }
    

    public void decreaseLife() {
		lifeTicks--; 
		if (isDead()) {
			color = firefighterDiedColor;
		}
	}
    
    public int directionSpread(String wind) {
    	if (wind == null) return 0;
    	else if (wind.equalsIgnoreCase("N"))  return 1;
	    else if (wind.equalsIgnoreCase("NE")) return 2;
	    else if (wind.equalsIgnoreCase("E"))  return 3;
	    else if (wind.equalsIgnoreCase("SE")) return 4;
	    else if (wind.equalsIgnoreCase("S"))  return 5;
	    else if (wind.equalsIgnoreCase("SW")) return 6;
	    else if (wind.equalsIgnoreCase("W"))  return 7;
	    else return 8;
	}
    
    @Override
    public void draw(SimGraphics g) {
        g.drawFastCircle(color);
    }
        
    public void fightFire() {
    	
    	//se a celula onde o bombeiro esta esta a arder, combate essa primerio
    	if (isOnBurningCell()) {
    		Cell c = (Cell) space.getObjectAt(p.x, p.y);
    		c.increaseWaterTicks();
    		return;
    	}
    	
    	@SuppressWarnings("rawtypes")
		Vector v = space.getMooreNeighbors(p.x, p.y, false);
    	
    	// ve qual o neighbour mais perto de ser extincto
    	int max = 0;
    	for (int i = 0; i < v.size(); i++) {
    		Cell c = (Cell) v.get(i);
    		if (c.isBurning() && c.getWaterTicks() > max)
    			max = c.getWaterTicks();
    	}
    		
    	for (int i = 0; i < v.size(); i++) {
    		Cell c = (Cell) v.get(i);
    		if (c.isBurning() && c.getWaterTicks() == max) {				
				c.increaseWaterTicks();				
				return;
    		}  		
    	}
    	
    	//visao alargada
    	Cell c = fireAroundAgent();
    	if (c != null) {
    		pgoto = c.getPoint();
    	}
    		  		
    }
    
    public static void setWind(String s) {
    	sWind = s;    	
    }
    
    public Cell fireAroundAgent() {
		
		int visao = 2;
		
		for (int xv = -visao; xv <= visao; xv++)
			for (int yv = -visao; yv <= visao; yv++) {
				if (xv != -2 || xv != 2 || yv != 2 || yv != -2)
					if (space.getObjectAt(p.x + xv, p.y + yv) instanceof Cell) {
						Cell c = (Cell) space.getObjectAt(p.x + xv, p.y + yv);
						if (c.isBurning())
							return c;
						}
			}	
		return null;
	}
    
    public void firebreak() {
				
		//TODO mudar compare atraves de strings
		
		if (p.toString().equals(pfbi.toString())) {
			firebreakStarted = true;
			pgoto = pfbf;
		}

		if (firebreakStarted) {
			cut();
		}
		
		if (p.toString().equals(pfbf.toString())) {
			setFirebreak(false);
			pgoto = null;
			firebreakStarted = false;
			firebreakFinished = true;
		}
	}

    public Color getColor() {
        return color;
    }
    
    public Point getBackFirePoint(Point origin, int radius, int nFirefighter, int nFirefighters, String sWind) {
    	
    	if (nFirefighters == 1) return origin;
    	
    	Point ret;
        
        int xMove = 0;
        int yMove = 0;
        
        int dir = directionSpread(sWind);
        
        int plus = radius;
        
        switch (dir) {
        	        
        //Norte
        case 1:
            xMove = origin.x - plus/2 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            yMove = origin.y + plus/5;
            break;
        
        //Nordeste
        case 2:
            xMove = origin.x - (5*plus)/12 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            yMove = origin.y - plus/12 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            break;

        //Este
        case 3:
            xMove = origin.x - plus/5;
            yMove = origin.y - plus/2 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;
          
        //Sudeste
        case 4:
            xMove = origin.x - (5*plus)/12 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            yMove = origin.y + (plus/12) - ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            break;
        
        //Sul
        case 5:
            xMove = origin.x - plus/2 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            yMove = origin.y - plus/5;
            break;
      
        //Sudoeste
        case 6:
            xMove = origin.x - plus/12 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            yMove = origin.y - ((5*plus)/12) + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            break;
            
    	//Oeste
        case 7:
            xMove = origin.x + plus/5;
            yMove = origin.y - plus/2 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;
            
    	//Noroeste
        case 8://
            xMove = origin.x + ((5*plus)/12) - ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            yMove = origin.y - plus/12 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/4;
            break;
           
        default:
        	break;
        	
        }
                       
        ret = new Point(xMove, yMove);
        
        return ret;
    }

    public Point getFrontFirePoint(Point origin, int radius, int nFirefighter, int nFirefighters, String sWind, int plus) {
		
		if (nFirefighters == 1) return origin;
    	
    	Point ret;
        
        int xMove = 0;
        int yMove = 0;
        
        int dir = directionSpread(sWind);
        
//        int plus = radius;
        
        switch (dir) {
        	        
        //Norte
        case 1:
            xMove = origin.x - plus + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) );
            yMove = origin.y - plus;
            break;
        
        //Nordeste
        case 2:
            xMove = origin.x + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            yMove = origin.y - (plus) + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;

        //Este
        case 3:
            xMove = origin.x + (plus - plus/3);
            yMove = origin.y - plus/2 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;
          
        //Sudeste
        case 4:
            xMove = origin.x + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            yMove = origin.y + plus - ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;
        
        //Sul
        case 5:
            xMove = origin.x - plus/2 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            yMove = origin.y + (plus-plus/3);
            break;
      
        //Sudoeste
        case 6:
            xMove = origin.x - plus + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            yMove = origin.y + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;
            
    	//Oeste
        case 7:
            xMove = origin.x - (plus-plus/3);
            yMove = origin.y - plus/2 + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;
            
    	//Noroeste
        case 8://
            xMove = origin.x - ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            yMove = origin.y - plus + ( nFirefighter * ( (2*radius)/(nFirefighters-1)) )/2;
            break;
           
        default:
        	break;
        	
        }
                       
        ret = new Point(xMove, yMove);
        
        return ret;
    }

	public int getIdleTicks() {
    	return idleTicks;
    }
	
	public Point getPoint() {
		return p;
	}
	
	@Override
    public int getX() {
        return p.x;
    }
	
	@Override
    public int getY() {
        return p.y;
    }
	
	public void goTo() {
    	
    	if (neighboursOnFire()) {
    		pgoto = null;
    		if (firebreak) {
    			setFirebreak(false);
    			firebreakStarted = false;    			    			
    		}
    		return;
    	}

    	if (p.x == pgoto.x && p.y == pgoto.y) {
    		pgoto = null;
    		return;
    	}
    	
    	// bombeiro que esta a cortar corta fogo so anda para a frente quando cortar a arvore toda
    	Cell c = (Cell) space.getObjectAt(p.x, p.y);
    	if (firebreakStarted && !c.isCut())
    		return;
	
		int xDelta = pgoto.x - p.x;
		int yDelta = pgoto.y - p.y;
	
		int xMove, yMove;
		if (xDelta > 0) xMove = 1; else if (xDelta == 0) xMove = 0; else xMove = -1;
		if (yDelta > 0) yMove = 1; else if (yDelta == 0) yMove = 0; else yMove = -1;	
				
		walk(xMove, yMove);		    	
    }
	
	public boolean hasFire() {
		return fireAroundAgent() != null;
	}
	
	public boolean isFirebreaker() {
		return firebreak;
	}
	
	public boolean hasGoTo() {
		return pgoto != null;
	}
	
	public boolean isAlive() {
		return lifeTicks != 0;
	}
		
	public boolean isDead() {
		return lifeTicks == 0;
	}
	
	public boolean isIdle() {
		return idleTicks >= maxIdleTicks;
	}
	
	public boolean isInjured() {
    	return lifeTicks != maxLifeTicks && lifeTicks != 0;
    }

    public boolean isOnBurningCell() {
		Cell c = (Cell) space.getObjectAt(p.x, p.y);
		return c.isBurning();
	}
    
    public boolean neighboursOnFire() {
    	
    	@SuppressWarnings("rawtypes")
		Vector v = space.getMooreNeighbors(p.x, p.y, false);
    	
    	for (int i = 0; i < v.size(); i++) { 
			Cell c = (Cell) v.get(i);
    			if (c.getState().equals(Cell.State.burning))
    				return true;
    	}
		return false;
    }
    
    public void setColor(Color c) {
    	color = c;
    }
    
    public void setFirebreak(boolean b) {
    	
    	firebreak = b;
    	
    	if (firebreak)
    		color = firefighterFirebreakColor;
    	else
    		color = firefighterNormalColor;
    }
    
    public void setFirebreakPointF(Point p) {
    	pfbf = p;
    	checkBoundaries(pfbf);
    }
    
    public void checkBoundaries(Point p) {
    	if (p.x > 99) p.x = 99;
		if (p.y > 99) p.y = 99;
		if (p.x < 0) p.x = 0;
		if (p.y < 0) p.y = 0;
    }
    
    public void setFirebreakPointI(Point p) {
    	pfbi = p;
    	checkBoundaries(pfbi);
    }
    
    public void setGoTo(Point p) {
    	pgoto = p;
    	checkBoundaries(pgoto);
    }
    
    public void walk(int xMove, int yMove) {
    	
    	int xf = p.x + xMove;
    	int yf = p.y + yMove;
    	
    	Cell ci = (Cell) space.getObjectAt(p.x, p.y);
    	Cell cf = (Cell) space.getObjectAt(xf, yf);
    	    	
    	if (!cf.hasFirefighter()) {
    		p.x += xMove;
    		p.y += yMove;
    	}
    	
    	else {
    		
    		int rx = random.nextInt(3);
    		int ry = random.nextInt(3);
    		
    		if (rx == 0) xMove = -1;
    		else if (rx == 1) xMove = 0;
    		else if (rx == 2) xMove = 1;
    		
    		if (ry == 0) yMove = -1;
    		else if (ry == 1) yMove = 0;
    		else if (ry == 2) yMove = 1;
    		
    		p.x += xMove;
    		p.y += yMove;
    		
    	}
    	
    	cf.setHasFirefighter(true);
		ci.setHasFirefighter(false);
    }
    
    public boolean hasFinishedFirebreak() {
    	return firebreakFinished;
    }
    
    public Point getMixedPoint(Point origin, int radius, int nFirefighter, int nFirefighters, String sWind, int plus) {
    	if (nFirefighter < nFirefighters/2)
    		return getFrontFirePoint(origin, radius, ((nFirefighters/2)-1)+nFirefighter, nFirefighters, sWind, plus);
    	else
    		return getBackFirePoint(origin, radius, nFirefighters-nFirefighter, nFirefighters, sWind);
    }
}
